package com.sprouting.ops.controller;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sprouting.ops.annotations.invoke.IdempotentSingle;
import com.sprouting.ops.interfaces.AddGroup;
import com.sprouting.ops.interfaces.ConstantBase;
import com.sprouting.ops.interfaces.UpdGroup;
import com.sprouting.ops.model.DromaraFileStorage;
import com.sprouting.ops.model.OpsKnowledge;
import com.sprouting.ops.model.ResultJson;
import com.sprouting.ops.model.qo.OpsKnowledgeQo;
import com.sprouting.ops.service.DromaraFileStorageService;
import com.sprouting.ops.service.OpsKnowledgeService;
import com.sprouting.ops.utils.CheckTool;
import com.sprouting.ops.utils.EntityTool;
import com.sprouting.ops.utils.IdTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * 知识中心(OpsKnowledge)(OPS_KNOWLEDGE) 表控制层
 *
 * @author ：lx（长沙麓谷）
 * @date 创建时间：2025-08-21 22:45:33
 */
@Slf4j
@RestController
@RequestMapping("opsKnowledge")
public class OpsKnowledgeController {

    @Resource
    private OpsKnowledgeService opsKnowledgeService;
    @Resource
    private DromaraFileStorageService dromaraFileStorageService;


    /**
     * 获取知识库-最高评分的知识库
     */
    @PostMapping("getHomeKnowledge")
    public ResultJson getHomeKnowledge(){
        return ResultJson.success(opsKnowledgeService.getHomeKnowledge());
    }

    /**
     * 分页查询知识中心
     * @param query 查询参数
     * @return
     */
    @PostMapping("getListPage")
    public ResultJson getListPage(OpsKnowledgeQo query){
        if (query == null){
            query = new OpsKnowledgeQo();
        }
        Page page = query.getPage();
        return ResultJson.success(opsKnowledgeService.getListPage(page, query));
    }

    /**
     * 删除知识中心
     * @param id 知识中心id
     * @return
     */
    @PostMapping("delOpsKnowledge")
    @IdempotentSingle
    public ResultJson delOpsKnowledge(String id){
        if (CheckTool.checkNull(id)){
            return ResultJson.errorCheck();
        }

        List<DromaraFileStorage> bindList = dromaraFileStorageService.getByBindId(id);

        opsKnowledgeService.delOpsKnowledge(id, bindList);
        return ResultJson.success();
    }

    /**
     * 点击踩或者喜欢
     * @param id 知识库id
     * @param isLike 是否喜欢，true 表示喜欢
     * @return
     */
    @PostMapping("clickLikeOrDislike")
    public ResultJson clickLikeOrDislike(String id, Boolean isLike){
        if (CheckTool.checkNull(id)){
            return ResultJson.errorCheck();
        }
        if (isLike == null){
            return ResultJson.errorCheck();
        }
        // 这里不做复杂，不然还要一人一票

        OpsKnowledge db = opsKnowledgeService.getById(id);
        if (isLike){
            db.setLikeNum(db.getLikeNum() + 1);
        } else {
            db.setDislikesNum(db.getDislikesNum() + 1);
        }
        // 重新计算得分
        int score = calculateFinalScoreAsInt(db.getLikeNum(), db.getDislikesNum());
        db.setScore(score);
        opsKnowledgeService.updateById(db);
        return ResultJson.success();
    }

    /**
     * 更新阅读次数
     * @param id 知识库id
     * @return
     */
    @PostMapping("updReadNum")
    public ResultJson updReadNum(String id){
        if (CheckTool.checkNull(id)){
            return ResultJson.errorCheck();
        }

        OpsKnowledge db = opsKnowledgeService.getById(id);
        db.setReadNum(db.getReadNum() + 1);
        opsKnowledgeService.updateById(db);
        return ResultJson.success(db);
    }

    /**
     * 更新知识中心
     * @param bo 修改参数
     * @return
     */
    @PostMapping("updOpsKnowledge")
    @IdempotentSingle
    public ResultJson updOpsKnowledge(@Validated(UpdGroup.class) OpsKnowledge bo){
        String msg = checkOpsKnowledge(bo);
        if (msg != null){
            return ResultJson.error(msg);
        }

        EntityTool.updBase(bo);

        // 提取Markdown的数据
        // 修改的时候也要提取，然后转换为生产的数据，这里要注意的是，修改的时候，不考虑去删除旧的数据的问题，到时候删除的时候，直接使用objId统一处理即可，因为这个数据量还是比较小的，没必要找的旧的去删，新的去加，搞麻烦了
        List<DromaraFileStorage> list = getMarkdownImage(bo);

        opsKnowledgeService.updOpsKnowledge(bo, list);
        return ResultJson.success(bo);
    }

    /**
     * 保存知识中心
     * @param bo 保存参数
     * @return
     */
    @PostMapping("saveOpsKnowledge")
    @IdempotentSingle
    public ResultJson saveOpsKnowledge(@Validated(AddGroup.class) OpsKnowledge bo){
        String msg = checkOpsKnowledge(bo);
        if (msg != null){
            return ResultJson.error(msg);
        }
        if (bo.getStatus() == null){
            bo.setStatus(ConstantBase.KnowledgeStatus.OPEN);
        }
        if (bo.getReadNum() == null){
            bo.setReadNum(0);
        }
        if (bo.getLikeNum() == null){
            bo.setLikeNum(0);
        }
        if (bo.getDislikesNum() == null){
            bo.setDislikesNum(0);
        }
        if (bo.getScore() == null){
            bo.setScore(3);
        }

        bo.setId(IdTool.getId());
        EntityTool.saveBase(bo);

        // 提取Markdown的数据
        List<DromaraFileStorage> list = getMarkdownImage(bo);

        opsKnowledgeService.saveOpsKnowledge(bo, list);
        return ResultJson.success(bo);
    }

    /**
     * 获取图片信息
     * @param knowledge 知识库
     * @return 提取到的图片信息用于转正式
     */
    private List<DromaraFileStorage> getMarkdownImage(OpsKnowledge knowledge) {
        List<DromaraFileStorage> list = new ArrayList<>();
        List<DromaraFileStorage> detailedlList = dromaraFileStorageService.getMarkdownImagesUrlToFileStorage(knowledge.getContent(), knowledge.getId(), ConstantBase.FileSourceType.KNOWLEDGE_CONTENT);
        if (CollUtil.isNotEmpty(detailedlList)){
            list.addAll(detailedlList);
        }

        return list;
    }

    /**
     * 知识中心数据校验
     * @param bo 知识中心 校验数据
     * @return 非null则有错误
     */
    private String checkOpsKnowledge(OpsKnowledge bo){

        return null;
    }

    /**
     * 使用威尔逊区间下限法计算标准化得分（0到1之间）
     * 威尔逊区间法解决的问题：
     *      传统简单平均（喜欢数/总票数）的缺陷
     *          小样本失真​​：只有1个喜欢0个踩的项目会得到100%好评率（5分）
     *          不公平比较​​：100喜欢/10踩（90.9%）的项目和10喜欢/1踩（90.9%）的项目得分相同
     *          ​​缺乏统计置信度​​：无法反映评分的不确定性
     * @param likes 喜欢的数量
     * @param dislikes 踩的数量
     * @return 标准化得分（0-1之间）
     */
    public static double calculateWilsonScore(int likes, int dislikes) {
        // 置信水平对应的Z值（95%置信度）
        final double z = 1.96;

        int n = likes + dislikes;

        // 处理无投票的情况
        if (n == 0) {
            // 或者返回0.5，表示中性评分
            return 0.0;
        }

        // 好评率
        double p = (double) likes / n;

        // 威尔逊区间公式计算
        double denominator = 1 + Math.pow(z, 2) / n;
        double centerAdjusted = p + Math.pow(z, 2) / (2 * n);
        double adjustedStdDev = Math.sqrt(
                (p * (1 - p) + Math.pow(z, 2) / (4 * n)) / n
        );

        // 计算下限
        double lowerBound = (centerAdjusted - z * adjustedStdDev) / denominator;

        // 确保分数不会低于0
        return Math.max(0.0, lowerBound);
    }

    /**
     * 将威尔逊得分映射到1-5分制，返回整数
     * @param likes 喜欢的数量
     * @param dislikes 踩的数量
     * @return 1-5之间的整数最终得分
     */
    public static int calculateFinalScoreAsInt(int likes, int dislikes) {
        double wilsonScore = calculateWilsonScore(likes, dislikes);

        // 将0-1的得分映射到1-5分：1 + (比例 * 4)
        double finalScore = 1 + wilsonScore * 4;

        // 方案1：四舍五入到最接近的整数（最推荐）
        return (int) Math.round(finalScore);

        // 方案2：向下取整（保守估计）
        // return (int) Math.floor(finalScore);

        // 方案3：向上取整（乐观估计）
        // return (int) Math.ceil(finalScore);
    }

}
